/**************************************************************************
 * simpletun.c                                                            *
 *                                                                        *
 * A simplistic, simple-minded, naive tunnelling program using tun/tap    *
 * interfaces and TCP. DO NOT USE THIS PROGRAM FOR SERIOUS PURPOSES.      *
 *                                                                        *
 * You have been warned.                                                  *
 *                                                                        *
 * (C) 2010 Davide Brini.                                                 *
 *                                                                        *
 * DISCLAIMER AND WARNING: this is all work in progress. The code is      *
 * ugly, the algorithms are naive, error checking and input validation    *
 * are very basic, and of course there can be bugs. If that's not enough, *
 * the program has not been thoroughly tested, so it might even fail at   *
 * the few simple things it should be supposed to do right.               *
 * Needless to say, I take no responsibility whatsoever for what the      *
 * program might do. The program has been written mostly for learning     *
 * purposes, and can be used in the hope that is useful, but everything   *
 * is to be taken "as is" and without any kind of warranty, implicit or   *
 * explicit. See the file LICENSE for further details.                    *
 *************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <linux/if_tun.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <arpa/inet.h> 
#include <sys/select.h>
#include <sys/time.h>
#include <errno.h>
#include <stdarg.h>
#include <iostream>
#include <arpa/inet.h>  // 包含 ntohl 的头文件
#include <netinet/in.h> // 提供 IP 相关的结构体#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <regex.h>

using namespace std;
/* buffer for reading from tun/tap interface, must be >= 1500 */
#define BUFSIZE 2000   
#define CLIENT 0
#define SERVER 1
#define PORT 55559
typedef unsigned char       BYTE; //1B
typedef unsigned short      WORD;//2
typedef unsigned long       DWORD;

typedef struct tIPPackHead//20B
{

    BYTE ver_hlen;      //IP协议版本和IP首部长度。高4位为版本，低4位为首部的长度(单位为4bytes)
    BYTE byTOS;       //服务类型
    WORD wPacketLen; //IP包总长度。包括首部，单位为byte。[Big endian]
    WORD wSequence;    //标识，一般每个IP包的序号递增。[Big endian]

    union
    {
        WORD Flags; //标志
        WORD FragOf;//分段偏移
    };
    BYTE byTTL;         //生存时间 
    BYTE byProtocolType; //协议类型，见PROTOCOL_TYPE定义
    WORD wHeadCheckSum;    //IP首部校验和[Big endian]
    unsigned int dwIPSrc;         //源地址
    unsigned int dwIPDes;         //目的地址
    //BYTE Options;          //选项
} IP_HEAD;


int debug;
char* progname;
int cnt;


// 读取并处理配置文件的函数




void my_err(const char* format, const char* arg) {
    std::cerr << format << arg << std::endl;
}



#define MAX_LINE_LENGTH 256
#define MAX_OPTIONS 10
#define MAX_COMMANDS 10

// Function to execute commands
void execute_commands(char* commands[], int num_commands) {
    for (int i = 0; i < num_commands; ++i) {
        printf("Executing: %s\n", commands[i]);
        int result = system(commands[i]);

        if (result == 0) {
            printf("Command executed successfully.\n");
        }
        else {
            fprintf(stderr, "Command execution failed with status: %d\n", result);
        }
    }
}

// Function to read configuration file and process options/commands
void read_tun_config(const char* config_file, char* device, char* server_ip,
    char* local_ip, char* commands[], int* num_commands) {
    FILE* file = fopen(config_file, "r");
    if (file == NULL) {
        fprintf(stderr, "Error opening file: %s\n", config_file);
        return;
    }

    char line[MAX_LINE_LENGTH];
    int in_options_section = 0;
    int in_command_section = 0;
    char options[MAX_OPTIONS][2][MAX_LINE_LENGTH];  // store key-value pairs for options
    int num_options = 0;

    // Read file line by line
    while (fgets(line, sizeof(line), file)) {
        // Remove newline character
        line[strcspn(line, "\n")] = 0;

        // Skip empty lines or comments
        if (line[0] == '\0' || line[0] == '#') {
            continue;
        }

        // Detect section headers
        if (strstr(line, "options") != NULL) {
            in_options_section = 1;
            in_command_section = 0;
            continue;
        }
        if (strstr(line, "command") != NULL) {
            in_command_section = 1;
            in_options_section = 0;
            continue;
        }


        // Process options section
        if (in_options_section) {
            char key[MAX_LINE_LENGTH], value[MAX_LINE_LENGTH];
            if (sscanf(line, " %255[^:]: %255[^\n];", key, value) == 2) {
                // Remove trailing spaces and semicolons from value
                int len = strlen(value);
                while (len > 0 && (value[len - 1] == ' ' || value[len - 1] == ';')) {
                    value[len - 1] = '\0';
                    len--;
                }

                // Store options as key-value pairs
                if (num_options < MAX_OPTIONS) {
                    strncpy(options[num_options][0], key, sizeof(options[num_options][0]));
                    strncpy(options[num_options][1], value, sizeof(options[num_options][1]));
                    num_options++;
                }

                if (strcmp(key, "device") == 0) {
                    strncpy(device, value, MAX_LINE_LENGTH);
                }
                else if (strcmp(key, "server_ip") == 0) {

                    strncpy(server_ip, value, MAX_LINE_LENGTH);
                }
                else if (strcmp(key, "local_ip") == 0) {

                    strncpy(local_ip, value, MAX_LINE_LENGTH);
                }
            }
        }



        // Process command section
        if (in_command_section) {
            // Remove the trailing semicolon
            if (line[strlen(line) - 1] == ';') {
                line[strlen(line) - 1] = '\0';
            }

            // Replace $(variable) with actual value from options
            char command[MAX_LINE_LENGTH];
            strncpy(command, line, sizeof(command));

            regex_t regex;
            regmatch_t match;
            regcomp(&regex, R"(\$\((\w+)\))", REG_EXTENDED);  // Pattern to match $(variable)

            char* pos = command;
            while (regexec(&regex, pos, 1, &match, 0) == 0) {
                // Extract the variable name
                char var_name[MAX_LINE_LENGTH];
                int length = match.rm_eo - match.rm_so;
                strncpy(var_name, pos + match.rm_so + 2, length - 3);  // Skip the $(

                var_name[length - 3] = '\0';


                // Replace with corresponding value from options
                for (int i = 0; i < num_options; i++) {
                    if (strcmp(options[i][0], var_name) == 0) {
                        char* var_pos = pos + match.rm_so;
                        size_t value_len = strlen(options[i][1]);
                        memmove(var_pos + value_len, var_pos + length, strlen(var_pos + length) + 1);
                        memcpy(var_pos, options[i][1], value_len);
                        break;
                    }
                }

                pos = pos + match.rm_eo;  // Move position forward
            }

            regfree(&regex);

            // Store the command to the commands array
            if (*num_commands < MAX_COMMANDS) {
                commands[*num_commands] = strdup(command);  // Copy command to array
                (*num_commands)++;
            }
        }
    }

    fclose(file);
}



int DecodeIP(char* buf, int len)
{
    int n = len;
    if (n >= sizeof(IP_HEAD))
    {
        IP_HEAD iphead;
        iphead = *(IP_HEAD*)buf;
        struct in_addr src, dest;
        src.s_addr = iphead.dwIPSrc;
        dest.s_addr = iphead.dwIPDes;
        cout << "第 " << cnt++ << " 个IP数据包信息：" << endl;


        cout << "协议版本:" << (iphead.ver_hlen >> 4) << endl;
        cout << "首部长度:" << ((iphead.ver_hlen & 0x0F) << 2) << endl;//单位为4字节
        cout << "服务类型:Priority: " << (iphead.byTOS >> 5) << ",Service: " << ((iphead.byTOS >> 1) & 0x0f) << endl;
        cout << "IP包总长度:" << ntohs(iphead.wPacketLen) << endl; //网络字节序转为主机字节序
        cout << "标识:" << ntohs(iphead.wSequence) << endl;
        cout << "标志位:" << "DF=" << ((iphead.Flags >> 14) & 0x01) << ",MF=" << ((iphead.Flags >> 13) & 0x01) << endl;
        cout << "片偏移:" << (iphead.FragOf & 0x1fff) << endl;
        cout << "生存周期:" << (int)iphead.byTTL << endl;
        cout << "协议类型:" << int(iphead.byProtocolType) << endl;
        cout << "首部校验和:" << ntohs(iphead.wHeadCheckSum) << endl;
        cout << "源地址:" << inet_ntoa(src) << endl;
        cout << "目的地址:" << inet_ntoa(dest) << endl;

        // cout << "目的地址:" << inet_ntoa(*(in_addr*)&iphead.dwIPDes) << endl;


        cout << "==============================================================" << endl << endl;
    }
    else {
        cout << "***包长不足：" << n << " < " << sizeof(IP_HEAD) << endl;
    }

    return 0;
}

/**************************************************************************
 * tun_alloc: allocates or reconnects to a tun/tap device. The caller     *
 *            must reserve enough space in *dev.                          *
 **************************************************************************/
int tun_alloc(char* dev, int flags) {

    struct ifreq ifr;
    int fd, err;
    char* clonedev = "/dev/net/tun";

    if ((fd = open(clonedev, O_RDWR)) < 0) {
        perror("Opening /dev/net/tun");
        return fd;
    }

    memset(&ifr, 0, sizeof(ifr));

    ifr.ifr_flags = flags;

    if (*dev) {
        strncpy(ifr.ifr_name, dev, IFNAMSIZ);
    }

    if ((err = ioctl(fd, TUNSETIFF, (void*)&ifr)) < 0) {
        perror("ioctl(TUNSETIFF)");
        close(fd);
        return err;
    }

    strcpy(dev, ifr.ifr_name);

    return fd;
}

/**************************************************************************
 * cread: read routine that checks for errors and exits if an error is    *
 *        returned.                                                       *
 **************************************************************************/
int cread(int fd, char* buf, int n) {
    int nread;

    if ((nread = read(fd, buf, n)) < 0) {
        perror("Reading data");
        exit(1);
    }
    //DecodeIP(buf, nread);


    printf("read len:%d -> ", nread);
    for (int i = 0;i < nread;i++) {
        printf("%x", buf[i]);
    }
    printf("\n");
    return nread;
}

/**************************************************************************
 * cwrite: write routine that checks for errors and exits if an error is  *
 *         returned.                                                      *
 **************************************************************************/
int cwrite(int fd, char* buf, int n) {

    int nwrite;
    if ((nwrite = write(fd, buf, n)) < 0) {
        perror("Writing data");
        exit(1);
    }

    printf("write: ");
    for (int i = 0;i < n;i++) {
        printf("%x", buf[i]);
    }
    printf("\n");
    return nwrite;
}

/**************************************************************************
 * read_n: ensures we read exactly n bytes, and puts them into "buf".     *
 *         (unless EOF, of course)                                        *
 **************************************************************************/


int read_n(int fd, char* buf, int n) {

    int nread, left = n;

    while (left > 0) {
        if ((nread = cread(fd, buf, left)) == 0) {
            return 0;
        }
        else {
            left -= nread;
            buf += nread;
        }
    }
    return n;
}

// 读取ipv4包头，获得需要继续读的长度
int read_ipv4_len_left(int fd, char* buf) {
    cout << "==read_ipv4_len_left==" << endl;
    int nread;
    nread = read_n(fd, buf, sizeof(IP_HEAD));
    cout << "==read_ipv4_len_left== -> read_n" << nread << endl;
    if (nread == 0) {
        cout << "read_ipv4_len_left get 0." << endl;
        return 0;
    }

    IP_HEAD iphead;
    iphead = *(IP_HEAD*)buf;
    DecodeIP(buf, nread);
    int len_to_read = ntohs(iphead.wPacketLen) - sizeof(IP_HEAD);
    cout << "此IP包总长度:    " << ntohs(iphead.wPacketLen) << endl;
    cout << "剩余要读取的长度:" << len_to_read << endl;
    return len_to_read;
}
/**************************************************************************
 * do_debug: prints debugging stuff (doh!)                                *
 **************************************************************************/
void do_debug(char* msg, ...) {

    va_list argp;

    if (debug) {
        va_start(argp, msg);
        vfprintf(stderr, msg, argp);
        va_end(argp);
    }
}

/**************************************************************************
 * my_err: prints custom error messages on stderr.                        *
 **************************************************************************/
void my_err(char* msg, ...) {

    va_list argp;

    va_start(argp, msg);
    vfprintf(stderr, msg, argp);
    va_end(argp);
}

/**************************************************************************
 * usage: prints usage and exits.                                         *
 **************************************************************************/
void usage(void) {
    fprintf(stderr, "Usage:\n");
    fprintf(stderr, "%s [-i <ifacename>] [-s|-c <serverIP>] [-p <port>] [-u|-a] [-d] [-f <config file>]\n", progname);
    fprintf(stderr, "%s -h\n", progname);
    fprintf(stderr, "\n");
    fprintf(stderr, "-i <ifacename>: Name of interface to use (optional)\n");
    fprintf(stderr, "-s|-c <serverIP>: run in server mode (-s), or specify server address (-c <serverIP>) (mandatory)\n");
    fprintf(stderr, "-p <port>: port to listen on (if run in server mode) or to connect to (in client mode), default 55555\n");
    fprintf(stderr, "-u|-a: use TUN (-u, default) or TAP (-a)\n");
    fprintf(stderr, "-d: outputs debug information while running\n");
    fprintf(stderr, "-h: prints this help text\n");
    fprintf(stderr, "-f <config file>: specify a config file\n");

    exit(1);
}


int main(int argc, char* argv[]) {
    std::cout << "argc: " << argc << std::endl; // 打印 argc 的值
    cout << "ip 包头:" << sizeof(IP_HEAD) << endl;
    int tap_fd, option;
    int flags = IFF_TUN;

    char if_name[IFNAMSIZ] = "";      // 网络接口名
    char remote_ip[16] = "";            /* dotted quad IP string */
    char local_ip[16] = "";             // IP地址

    char config_file[256] = "";

    char* commands[MAX_COMMANDS];
    int num_commands = 0;


    // std::vector<std::string> config_commands;
    int maxfd;
    uint16_t nread, nwrite, plength;
    char buffer[BUFSIZE];
    struct sockaddr_in local, remote;


    unsigned short int port = PORT;
    int sock_fd, net_fd, optval = 1;
    socklen_t remotelen;
    int cliserv = -1;    /* must be specified on cmd line */
    unsigned long int tap2net = 0, net2tap = 0;

    progname = argv[0];

    /* Check command line options */
    while ((option = getopt(argc, argv, "scp:uahdf:")) != -1) {
        switch (option) {
        case 'd':
            debug = 1;
            break;
        case 'h':
            usage();
            break;
            // case 'i':
            //     strncpy(if_name, optarg, IFNAMSIZ - 1);
            //     if_name[IFNAMSIZ - 1] = '\0';  // 确保终止符
            //     break;
        case 's':
            cliserv = SERVER;
            break;
        case 'c':
            cliserv = CLIENT;
            break;
        case 'p':
            port = atoi(optarg);
            break;
        case 'u':
            flags = IFF_TUN;
            break;
        case 'a':
            flags = IFF_TAP;
            break;
        case 'f':
            if (optarg != NULL) {
                strncpy(config_file, optarg, sizeof(config_file) - 1);
                config_file[sizeof(config_file) - 1] = '\0';  // 确保字符串终止符
            }
            else {
                my_err("Option -f requires a file path argument.\n");
                usage();
            }
            break;
        default:
            my_err("Unknown option %c\n", option);
            usage();
        }
    }



    cout << "optind: " << optind << endl;
    argv += optind;
    argc -= optind;

    // 如果传入了配置文件路径，读取配置文件
    if (strlen(config_file) > 0) {
        read_tun_config(config_file, if_name, remote_ip, local_ip, commands, &num_commands);
        // 执行配置文件中的命令
        // execute_config_commands(config_file, if_name, remote_ip, netmask, gateway);
    }
    cout << "argc: " << argc << endl;

    if (argc > 0) {
        my_err("Too many options!\n");
        usage();
    }

    if (*if_name == '\0') {
        my_err("Must specify interface name!\n");
        usage();
    }
    else if (cliserv < 0) {
        my_err("Must specify client or server mode!\n");
        usage();
    }
    else if ((cliserv == CLIENT) && (*remote_ip == '\0')) {
        my_err("Must specify server address!\n");
        usage();
    }


    /* initialize tun/tap interface */
    if ((tap_fd = tun_alloc(if_name, flags | IFF_NO_PI)) < 0) {
        my_err("Error connecting to tun/tap interface %s!\n", if_name);
        exit(1);
    }

    //执行命令启动设备和设置ip
    execute_commands(commands, num_commands);

    do_debug("Successfully connected to interface %s\n", if_name);

    if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("socket()");
        exit(1);
    }

    if (cliserv == CLIENT) {
        /* Client, try to connect to server */

        /* assign the destination address */
        memset(&remote, 0, sizeof(remote));
        remote.sin_family = AF_INET;
        remote.sin_addr.s_addr = inet_addr(remote_ip);
        remote.sin_port = htons(port);

        /* connection request */
        if (connect(sock_fd, (struct sockaddr*)&remote, sizeof(remote)) < 0) {
            perror("connect()");
            exit(1);
        }

        net_fd = sock_fd;
        do_debug("CLIENT: Connected to server %s\n", inet_ntoa(remote.sin_addr));

    }
    else {
        /* Server, wait for connections */

        /* avoid EADDRINUSE error on bind() */
        if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char*)&optval, sizeof(optval)) < 0) {
            perror("setsockopt()");
            exit(1);
        }

        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_addr.s_addr = htonl(INADDR_ANY);
        local.sin_port = htons(port);
        if (bind(sock_fd, (struct sockaddr*)&local, sizeof(local)) < 0) {
            perror("bind()");
            exit(1);
        }

        if (listen(sock_fd, 5) < 0) {
            perror("listen()");
            exit(1);
        }

        /* wait for connection request */
        remotelen = sizeof(remote);
        memset(&remote, 0, remotelen);
        if ((net_fd = accept(sock_fd, (struct sockaddr*)&remote, &remotelen)) < 0) {
            perror("accept()");
            exit(1);
        }

        do_debug("SERVER: Client connected from %s\n", inet_ntoa(remote.sin_addr));
    }

    /* use select() to handle two descriptors at once */
    maxfd = (tap_fd > net_fd) ? tap_fd : net_fd; // 确定最大文件描述符，tap_fd 和 net_fd 中较大的一个

    while (1) { // 进入无限循环
        int ret; // 声明返回值变量
        fd_set rd_set; // 声明文件描述符集合

        FD_ZERO(&rd_set); // 清空文件描述符集合
        FD_SET(tap_fd, &rd_set); // 将 tap_fd 加入集合
        FD_SET(net_fd, &rd_set); // 将 net_fd 加入集合

        ret = select(maxfd + 1, &rd_set, NULL, NULL, NULL); // 监听文件描述符变化，等待数据到达

        if (ret < 0 && errno == EINTR) { // 如果 select 被中断（例如信号），则继续循环
            continue; // 继续下一次循环
        }

        if (ret < 0) { // 如果 select 发生错误
            perror("select()"); // 打印错误信息
            exit(1); // 退出程序
        }

        if (FD_ISSET(tap_fd, &rd_set)) { // 如果 tap_fd 有数据可读
            printf("tun有数据可以读\n");
            /* 从 tun/tap 接口读取数据并写入网络 */
            nread = cread(tap_fd, buffer, BUFSIZE); // 从 tap_fd 读取数据到 buffer，最大长度为 BUFSIZE
            DecodeIP(buffer, nread);
            printf("tap recv ip packet ver：%x\n", (char)(*buffer)); // 输出接收到的数据包版本
            if ((char)(*buffer) == 0x45) { // 检查是否为 IPv4 数据包（版本 4，首部长度 5）
                tap2net++; // 增加 tap2net 计数器
                do_debug("TAP2NET %lu: Read %d bytes from the tap interface\n", tap2net, nread); // 调试输出读取的字节数

                nwrite = cwrite(net_fd, buffer, nread); // 将读取的数据写入网络
                do_debug("TAP2NET %lu: Written %d bytes to the network\n", tap2net, nwrite); // 调试输出写入的字节数
            }
            else {
                printf("Not ipv4 packet, drop this."); // 如果不是 IPv4 数据包，输出提示并丢弃数据包
            }
        }

        if (FD_ISSET(net_fd, &rd_set)) { // 如果 net_fd 有数据可读
            printf("socket有数据可以读\n");
            /* 从网络读取数据，并写入 tun/tap 接口
            * 首先读取长度，然后读取数据包 */

            /* 读取长度 */
            nread = read_ipv4_len_left(net_fd, (char*)buffer); // 从网络读取数据包的长度和ip头部
            if (nread == 0) { // 如果 nread 为 0，表示连接中断
                cout << "get nread==0, break." << endl; // 输出提示
                break; // 跳出循环
            }
            else {
                // 这里可以放置处理 nread 大于 0 的代码
            }

            net2tap++; // 增加 net2tap 计数器

            char* whole_packet = (char*)malloc(sizeof(IP_HEAD) + nread); // 为完整数据包分配内存

            memcpy(whole_packet, buffer, sizeof(IP_HEAD)); // 复制 IP 头到完整数据包中

            /* 读取数据包 */
            nread = read_n(net_fd, buffer, nread); // 从网络读取数据包内容
            memcpy(whole_packet + sizeof(IP_HEAD), buffer, nread); // 将数据包内容复制到完整数据包中
            do_debug("NET2TAP %lu: Read %d bytes from the network\n", net2tap, nread); // 调试输出读取的字节数

            /* 现在 buffer[] 包含完整的数据包，写入 tun/tap 接口 */
            nwrite = cwrite(tap_fd, (char*)whole_packet, sizeof(IP_HEAD) + nread); // 将完整数据包写入 tap_fd
            do_debug("NET2TAP %lu: Written %d bytes to the tap interface\n", net2tap, nwrite); // 调试输出写入的字节数
        }
    }

    return(0); // 返回 0，表示程序正常结束
}
